#include "config.h"

#include <cstdio>
#include <iostream>
#include <string>

using namespace cfg;

// 示例：从文件直接加载配置并与规则联动的负例/边界用例
// 目标：
//  1) 直接 include 两份配置文件（对象根与标量根），展示合并与非对象兜底写入 key "value"。
//  2) 从文件加载 Schema；验证若干负例与边界（必填缺失、类型错配、枚举错误、pattern 边界）。
// 说明：
//  - 需启用 CFG_ENABLE_YAML。
//  - 示例动态在当前工作目录下创建临时 .yaml/.json 文件。

// 函数: create_file
// 作用: 在当前工作目录创建指定文件并写入给定内容, 返回创建的文件路径; 若创建失败则返回空字符串。
static std::string
create_file(const std::string & filename, const char * content)
{
    FILE * fp = std::fopen(filename.c_str(), "w");
    if (!fp)
        return {};
    std::fputs(content, fp);
    std::fclose(fp);
    return filename;
}

// 函数: print_error
// 作用: 以结构化方式打印一次配置/Schema 校验错误,
// 包含错误码、消息、路径、期望/实际类型、取值范围与正则等上下文信息。
static void
print_error(const ConfigError & e)
{
    std::cerr << "[" << static_cast<int>(e.code) << "] " << e.message;
    if (!e.path.empty())
    {
        std::cerr << " path=";
        for (size_t i = 0; i < e.path.size(); ++i)
        {
            if (i)
                std::cerr << ".";
            std::cerr << e.path[i];
        }
    }
    if (e.expected_type)
        std::cerr << " expected_type=" << static_cast<int>(*e.expected_type);
    if (e.actual_type)
        std::cerr << " actual_type=" << static_cast<int>(*e.actual_type);
    if (e.min_value)
        std::cerr << " min=" << *e.min_value;
    if (e.max_value)
        std::cerr << " max=" << *e.max_value;
    if (e.expected_pattern)
        std::cerr << " pattern=\"" << *e.expected_pattern << "\"";
    std::cerr << "\n";
}

// 函数: main
// 作用: 演示从 YAML/JSON 文件直接加载 Schema 与配置, 先构造负例触发多类规则错误,
// 再修正为边界可接受值并验证读取结果。
int
main()
{
#ifndef CFG_ENABLE_YAML
    std::cerr << "CFG_ENABLE_YAML not enabled\n";
    return 1;
#else
    // 1) 写入 schema 文件（包含必填、类型、范围、枚举、pattern）
    const std::string schema_path =
        create_file("schema_cases.yaml",
                    "type: object\n"
                    "properties:\n"
                    "  host:\n"
                    "    type: string\n"
                    "  port:\n"
                    "    type: integer\n"
                    "    minimum: 1\n"
                    "    maximum: 65535\n"
                    "  mode:\n"
                    "    type: string\n"
                    "    enum: [dev, prod]\n"
                    "  email:\n"
                    "    type: string\n"
                    "    pattern: '^[\\w.-]+@[\\w.-]+\\.[A-Za-z]{2,}$'\n"
                    "  min_only:\n"
                    "    type: integer\n"
                    "    minimum: 10\n"
                    "  name:\n"
                    "    type: string\n"
                    "required: [host, port]\n");

    // 2) 写入两份配置：对象根与标量根
    const std::string cfg_obj = create_file("cfg_object.yaml",
                                            "host: localhost\n"
                                            "port: 70000\n" // 超出最大值，用于负例
                                            "mode: stage\n" // 不在枚举
                                            "email: user_at_example.com\n" // 不匹配 pattern
                                            "min_only: 5\n"                // 小于最小值边界
                                            "name: 123\n" // 类型错配: 期望字符串
    );

    const std::string cfg_scalar = create_file("cfg_value.json", "42\n"); // 非对象根，兜底 value

    // 3) 构建器：加载 schema，并 include 文件
    Builder b;
    b.load_json_schema_file(schema_path);
    b.include_file(cfg_obj).include_file(cfg_scalar);

    // 4) 负例：直接 freeze 校验，收集错误
    auto fr_bad = b.freeze();
    if (fr_bad)
    {
        std::cerr << "unexpected success\n";
        return 1;
    }
    std::cerr << "Bad case errors: " << fr_bad.error().size() << "\n";
    for (const auto & e : fr_bad.error())
        print_error(e);

    // 5) 修正边界：将 port 修正为边界最大值、mode 修正为枚举项、email 修正为合法 pattern, min_only
    // 修正为最小边界, name 修正为字符串
    Builder good;
    good.load_json_schema_file(schema_path);
    // 路径策略演示：基于 base_dir 解析相对路径
    good.set_include_path_mode(IncludePathMode::RelativeToBaseDir).set_include_base_dir(".");
    good.include_file("cfg_object.yaml"); // 使用相对路径+基准目录
    good.include_file("cfg_value.json");
    good.set("port", int64_t{65535});
    good.set("mode", std::string("prod"));
    good.set("email", std::string("user@example.com"));
    good.set("min_only", int64_t{10});
    good.set("name", std::string("ok"));

    auto fr_good = good.freeze();
    if (!fr_good)
    {
        std::cerr << "unexpected failure\n";
        for (const auto & e : fr_good.error())
            print_error(e);
        return 1;
    }

    View v(*fr_good);
    auto host = v.lookup("host").and_then([](const View & sv) { return sv.get_string(); });
    auto port = v.lookup("port").and_then([](const View & sv) { return sv.get_int64(); });
    auto value = v.lookup("value").and_then([](const View & sv) { return sv.get_int64(); });

    if (host && port && value)
        std::cout << "Fixed: host=" << *host << ", port=" << *port << ", value=" << *value << "\n";
    else
        std::cout << "Fixed with missing fields\n";

    return 0;
#endif
}
